package com.aaron.spring;

import com.aaron.annotation.RpcReference;
import com.aaron.annotation.RpcService;
import com.aaron.config.RpcServiceConfig;
import com.aaron.entity.RpcServiceProperties;
import com.aaron.extension.ExtensionLoader;
import com.aaron.factory.SingletonFactory;
import com.aaron.provider.ServiceProvider;
import com.aaron.provider.impl.ZkServiceProviderImpl;
import com.aaron.proxy.RpcClientProxy;
import com.aaron.remoting.transport.RpcRequestTransport;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;

/**
 * BeanPostProcessor(增强器，后置处理器）。此时实例化的bean还没有初始化
 * 可以在初始化bean之前对该bean进行一些操作
 */
@Slf4j
@Component
public class SpringBeanPostProcessor implements BeanPostProcessor {
    // 注册服务，添加服务，获取服务
    private final ServiceProvider serviceProvider;
    // 选择通信的方式，netty或socket
    private final RpcRequestTransport rpcClient;

    public SpringBeanPostProcessor() {
        // 获取serviceProvider对象，其实现了ServiceProviderImpl接口
        this.serviceProvider = SingletonFactory.getInstance(ZkServiceProviderImpl.class);
        // 获取拓展类实例，SPI拓展机制
        this.rpcClient = ExtensionLoader.getExtensionLoader(RpcRequestTransport.class).getExtension("netty");
    }

    // spring bean实例化之前调用，
    // 去判断类上是否有RpcService注解。如果有的话，就取出group 和version的值。
    // 然后，再调用ServiceProvider的publishService()方法发布服务即可
    @SneakyThrows
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean.getClass().isAnnotationPresent(RpcService.class)) {
            log.info("[{}] is annotated with  [{}]", bean.getClass().getName(), RpcService.class.getCanonicalName());
            // get RpcService annotation
            RpcService rpcService = bean.getClass().getAnnotation(RpcService.class);
            // build RpcServiceProperties
//            RpcServiceProperties rpcServiceProperties = RpcServiceProperties.builder()
//                    .group(rpcService.group()).version(rpcService.version()).build();
            RpcServiceConfig rpcServiceConfig = RpcServiceConfig.builder()
                    .group(rpcService.group())
                    .version(rpcService.version())
                    .service(bean).build();
            // 注解了@RpcService，发布这个服务
            serviceProvider.publishService(rpcServiceConfig);
        }
        return bean;
    }

    // spring bean实例化之后调用
    // 去遍历类上是否有RpcReference注解。如果有的话，我们就通过反射 将这个属性赋值。
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetClass = bean.getClass();
        Field[] declaredFields = targetClass.getDeclaredFields();

        for (Field declaredField : declaredFields) {
            RpcReference rpcReference = declaredField.getAnnotation(RpcReference.class);
            if (rpcReference != null) {
//                RpcServiceProperties rpcServiceProperties = RpcServiceProperties.builder()
//                        .group(rpcReference.group()).version(rpcReference.version()).build();
                RpcServiceConfig rpcServiceConfig = RpcServiceConfig.builder()
                        .group(rpcReference.group())
                        .version(rpcReference.version()).build();

                RpcClientProxy rpcClientProxy = new RpcClientProxy(rpcClient, rpcServiceConfig);
                // 获取代理对象
                Object clientProxy = rpcClientProxy.getProxy(declaredField.getType());
                // setAccessible(true)是Java语言访问检查，可以达到提升反射速度的目的
                declaredField.setAccessible(true);
                try {
                    declaredField.set(bean, clientProxy);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }
}
